﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Dotnet.Utils.Utility.ObjectExtensions.Str
{
    public class StrHelper
    {

        /// <summary>
        /// 将一个值转化为一个字符串用于表格输出
        /// </summary>
        /// <param name="o"><see cref="object"/>要用于转化的值</param>
        /// <returns><see cref="object"/>返回的字符串</returns>
        /// <example>
        /// <code>
        /// Color co = Color.Red;
        /// System.Console.Write(StrHelper.GetCommonTextForBind(co));
        /// </code>
        /// <b>输出：</b>Red
        /// </example>
        public static string GetCommonTextForBind(object o)
        {
            if (o == null)
            {
                return String.Empty;
            }
            if (o is DBNull)
            {
                return String.Empty;
            }
            return o.ToString();
        }

        /// <summary>
        /// 将一个日期格式的值转化为字符串用于表格输出
        /// </summary>
        /// <param name="o"><see cref="object"/>要用于转化的值</param>
        /// <param name="formatString"><see cref="string"/>日期格式字符串</param>
        /// <returns>返回的字符串</returns>
        /// <example>
        /// <code>
        /// decimal de = 1.33333m;
        /// System.Console.Write(StrHelper.GetDateTextForBind(de,"0.00"));
        /// </code>
        /// <b>输出：</b>1.33
        /// </example>
        public static string GetDateTextForBind(object o, string formatString)
        {
            if (o == null)
            {
                return String.Empty;
            }
            if (o is DBNull)
            {
                return String.Empty;
            }
            if (!(o is DateTime))
            {
                o.ToString();
            }
            try
            {
                return Convert.ToDateTime(o).ToString(formatString);
            }
            catch
            {
                return Convert.ToDateTime(o).ToString("yyyy-MM-dd");
            }
        }

        /// <summary>
        /// 生成显示输出字串
        /// </summary>
        /// <param name="Source"><see cref="string"/>，源字符串</param>
        /// <param name="MaxLength"><see cref="int"/>，最大长度</param>
        /// <returns><see cref="string"/>，返回生成字符串</returns>
        /// <remarks>
        /// 非ASII码字符站2个长度，若源字符串实际可视字数大于最大长度则按最大长度截断，后补充“...”
        /// </remarks>
        /// <example>
        /// <code>
        /// string sSrc = "action，最大长度截断";
        /// System.Console.Write(StrHelper.GetDisplayFormatString(sSrc,16));
        /// </code>
        /// <b>输出：</b>action，最大长度...
        /// </example>
        public static string GetDisplayFormatString(string Source, int MaxLength)
        {
            string Tmp = Source.Trim();
            byte[] bytes = System.Text.Encoding.Unicode.GetBytes(Tmp);
            int Index = 0;
            int i = 0;
            for (; i < Tmp.Length; i++)
            {
                if (bytes[2 * i + 1].ToString() != "0")
                {
                    Index += 2;
                }
                else
                {
                    Index += 1;
                }
                //大于等于最大长度
                if (Index >= MaxLength) break;
            }

            if (i == Tmp.Length)
            {//正常
                return Tmp;
            }
            else
            {//非正常
                if (i == Tmp.Length - 1 && Index <= MaxLength + 1)
                {
                    return Tmp;
                }
                return Tmp.Substring(0, i + 1) + "...";
            }
        }
        /// <summary>
        /// 生成截断字串
        /// </summary>
        /// <param name="Source"><see cref="string"/>，源字符串</param>
        /// <param name="MaxLength"><see cref="int"/>，最大长度</param>
        /// <returns><see cref="string"/>，返回生成字符串</returns>
        /// <remarks>
        /// 若源字串实际可视字数大于最大长度则按最大长度截断。
        /// </remarks>
        /// <example>
        /// <code>
        /// string sSrc = "action，最大长度截断";
        /// System.Console.Write(StrHelper.GetFormatString(sSrc,16));
        /// </code>
        /// <b>输出：</b>action，最大长度
        /// </example>
        public static string GetFormatString(string Source, int MaxLength)
        {
            if (Source.Length * 2 <= MaxLength) return Source;
            string Tmp = Source;
            byte[] bytes = System.Text.Encoding.Unicode.GetBytes(Tmp);
            int Index = 0;
            int i = 0;
            for (; i < Tmp.Length; i++)
            {
                if (bytes[2 * i + 1].ToString() != "0")
                {
                    Index += 2;
                }
                else
                {
                    Index += 1;
                }
                //大于等于最大长度
                if (Index >= MaxLength) break;
            }

            if (i == Tmp.Length)
            {//正常
                return Tmp;
            }
            else
            {//非正常
                if (i == Tmp.Length - 1 && Index <= MaxLength + 1)
                {
                    return Tmp;
                }
                return Tmp.Substring(0, i + 1);
            }
        }
        /// <summary>
        /// 获取字符串长度
        /// </summary>
        /// <param name="Source"><see cref="string"/>，源字符串</param>
        /// <returns><see cref="int"/>，返回字符串单字节长度</returns>
        /// <example>
        /// <code>
        /// string sSrc = "源字符串1";
        /// System.Console.Write(StrHelper.GetStringLength(sSrc));
        /// </code>
        /// <b>输出：</b>9
        /// </example>
        public static int GetStringLength(string Source)
        {
            byte[] bytes = System.Text.Encoding.Unicode.GetBytes(Source);
            int Index = 0;
            int i = 0;
            for (; i < Source.Length; i++)
            {
                if (bytes[2 * i + 1].ToString() != "0")
                {
                    Index += 2;
                }
                else
                {
                    Index += 1;
                }
            }
            return Index;
        }
        /// <summary>
        /// 将一个日期格式的值转化为字符串用于表格输出
        /// </summary>
        /// <param name="o"><see cref="object"/>，要用于转化的值</param>
        /// <returns><see cref="string"/>，返回的字符串</returns>
        /// <example>
        /// <code>
        /// System.Console.Write(StrHelper.GetDateTextForBind(DateTime.Now));
        /// </code>
        /// <b>输出：</b>2006-07-11
        /// </example>
        public static string GetDateTextForBind(object o)
        {
            return GetDateTextForBind(o, "yyyy-MM-dd");
        }

        /// <summary>
        /// 返回一个文件大小字符串
        /// </summary>
        /// <param name="iFileLength"><see cref="int"/>文件大小(字节为单位)</param>
        /// <returns><see cref="string"/>表示文件大小的字符串</returns>
        /// <example>
        /// <code>
        /// int ilength = 1024*1024*5;
        /// System.Console.Write(StrHelper.GetFileSize(ilength));
        /// </code>
        /// <b>输出：</b>5 MByte
        /// </example>
        public static string GetFileSize(int iFileLength)
        {
            const int M_Value = 1024 * 1024;
            const int K_Value = 1024;
            double sFileLength = iFileLength;

            if (iFileLength >= M_Value)     //MB
            {
                sFileLength = sFileLength / M_Value;
                return sFileLength.ToString("N") + " MByte";
            }
            else if (iFileLength >= K_Value)    //KB
            {
                sFileLength = sFileLength / K_Value;
                return sFileLength.ToString("N") + " KByte";
            }
            else                            //Byte
            {
                return sFileLength.ToString("N") + " Byte";
            }
        }

        /// <summary>
        /// 返回一个浮点数的小数点表示格式字符串
        /// </summary>
        /// <param name="fValue"><see cref="decimal"/>，浮点数</param>
        /// <param name="iRadixLength"><see cref="int"/>，小数个数</param>
        /// <param name="sZeroDefaultString"><see cref="string"/>，当浮点数为零时返回的字符串</param>
        /// <returns><see cref="string"/>，字符串</returns>
        /// <example>
        /// <code>
        /// decimal de = 1.5555m;
        ///  System.Console.Write(StrHelper.GetDecimalString(de,2,"0.0"));
        /// </code>
        /// <b>输出：</b>1.56
        /// </example>
        public static string GetDecimalString(decimal fValue, int iRadixLength, string sZeroDefaultString)
        {
            if (fValue == decimal.Zero)
                return sZeroDefaultString;
            if (iRadixLength <= 0)
                return StrHelper.RoundDecimal(fValue, 0).ToString();
            else
                return StrHelper.RoundDecimal(fValue, iRadixLength).ToString("f" + iRadixLength);
        }
        public static string GetDateString(object dValue, string sDefaultString)
        {
            if (dValue == null || dValue == DBNull.Value)
                return sDefaultString;
            if (dValue is DateTime)
            {
                if (((DateTime)dValue) == DateTime.MinValue)
                    return sDefaultString;
                return ((DateTime)dValue).ToShortDateString();
            }
            return sDefaultString;
        }
        /// <summary>
        /// 对于超过指定长度的字符串进行换行
        /// </summary>
        /// <param name="s"><see cref="string"/>，字符串</param>
        /// <param name="length"><see cref="int"/>，指定长度</param>
        /// <returns><see cref="string"/>，返回换行过的字符串的HTML</returns>
        /// <example>
        /// <code>
        /// string sSrc = "返回换行过的字符串的HTML";
        /// System.Console.Write(StrHelper.GetNewLineString(sSrc,4));
        /// </code>
        /// <b>输出：</b>返回换行&lt;BR&gt;过的字符&lt;BR&gt;串的HT&lt;BR&gt;ML
        /// </example>
        public static string GetNewLineString(string s, int length)
        {
            if (s.Length > length)
            {
                s = s.Substring(0, length) + "<br>" + GetNewLineString(s.Substring(length, s.Length - length), length);
            }
            return s;
        }
        /// <summary>
        /// 获取代表某个日期是星期几的字符串
        /// </summary>
        /// <param name="curDate"><see cref="System.DateTime"/>，日期</param>
        /// <returns><see cref="string"/>，返回代表指定的日期是星期几的字符串</returns>
        public static string GetDayOfWeekName(DateTime curDate)
        {
            switch (curDate.DayOfWeek)
            {
                case DayOfWeek.Sunday: return "周日";
                case DayOfWeek.Monday: return "周一";
                case DayOfWeek.Tuesday: return "周二";
                case DayOfWeek.Wednesday: return "周三";
                case DayOfWeek.Thursday: return "周四";
                case DayOfWeek.Friday: return "周五";
                case DayOfWeek.Saturday: return "周六";
                default: return string.Empty;
            }
        }

        /// <summary>
        /// 处理一个字符串为空的情形
        /// </summary>
        /// <param name="s"><see cref="string"/>，要进行处理的字符串。</param>
        /// <param name="defValue"><see cref="string"/>，当字符串为空时要返回的默认值</param>
        /// <returns>当字符串为空时返回指定的默认值，否则返回字符串本身</returns>
        public static string IsNull(string s, string defValue)
        {
            if (s == null)
                return defValue;
            return s;
        }

        /// <summary>
        /// 判断一个字符串是否是整数<see cref="System.Int32"/>。
        /// </summary>
        /// <param name="s">要进行判断的字符串。</param>
        /// <returns>返回指定的字符串是否时整数<see cref="System.Int32"/>。</returns>
        /// <remarks>返回说明：如果指定的字符串是整数<see cref="System.Int32"/>则返回<c>true</c>，否则返回<c>false</c>。</remarks>
        public static bool IsInt(string s)
        {
            //Int32有符号整数的格式请查看MSDN文档
            string sPattern = @"^\s*[+-]?\d+\s*$";
            return Regex.IsMatch(s, sPattern, RegexOptions.Compiled | RegexOptions.Singleline);
        }

        /// <summary>
        /// 判断一个字符串是否符合是十进制数<see cref="System.Decimal"/>。
        /// </summary>
        /// <param name="s">要进行判断的字符串。</param>
        /// <returns>返回指定的字符串是否是十进制数<see cref="System.Decimal"/>。</returns>
        /// <remarks>返回说明：如果指定的字符串是十进制数<see cref="System.Decimal"/>则返回<c>true</c>，否则返回<c>false</c>。</remarks>
        public static bool IsDecimal(string s)
        {
            //Decimal结构格式请查看MSDN文档
            //说明：1 小数点符号采用点号[.] 
            //      2 组分隔符采用逗号[,] 其中组的大小为3位
            //      3 没有考虑使用指数表示浮点数的情况
            //      因区域文化属性的设置可能会导致小数点符号、组分隔符及组大小等与本处所默认的不一样
            //      ，可以使用CultureInfo的NumberFormatInfo获取这些相关的属性，这里暂不考虑这种情况）
            string sPattern = @"^\s*[+-]?(\d+(\,\d{3})*)*((\d+\.?)|(\.?\d+))\d*\s*$";
            return Regex.IsMatch(s, sPattern, RegexOptions.Compiled | RegexOptions.Singleline);
        }

        /// <summary>
        /// 判断一个字符串是否符合特定的<see cref="System.DateTime"/>格式。
        /// </summary>
        /// <param name="s"><see cref="string"/>，要进行判断的字符串。</param>
        /// <returns>返回指定的字符串是否符合特定的<see cref="System.DateTime"/>格式。</returns>
        /// <remarks>返回说明：如果指定的字符串符合特定的<see cref="System.DateTime"/>格式则返回<c>true</c>，否则返回<c>false</c>。</remarks>
        public static bool IsDateTime(string s)
        {
            //匹配如下格式的日期 yyyy-MM-dd [hh:mi[:ss]] yyyy/MM/dd [hh:mi[:ss]] yyyyMMdd [hh:mi[:ss]]
            //其中[]括起的部分是可选的
            string sPattern = @"^\s*\d{1,4}[-/]?\d{1,2}[-/]?\d{1,2}(\s+[0-5]?\d\:[0-5]?\d(\:[0-5]?\d)?)?\s*$";
            //^\s*((\d{1,3}(\,\d{3})+)|(\d+))(\.\d+)?\s*$/;
            return Regex.IsMatch(s, sPattern, RegexOptions.Compiled | RegexOptions.Singleline);
        }
        public static int ParseInt(object objVal, int defValue)
        {
            return ParseInt("" + objVal, defValue);
        }
        /// <summary>
        /// 将一个字符串转换为它的等效<see cref="System.Int32"/>整数。
        /// </summary>
        /// <param name="s"><see cref="string"/>，要转换的字符串。</param>
        /// <param name="defValue"><see cref="int"/>，转换失败时返回的值。</param>
        /// <returns>返回指定字符串的等效<see cref="System.Int32"/>整数。</returns>
        /// <remarks>返回说明：如果指定的字符串无法转换为<see cref="System.Int32"/>整数则返回指定的默认值，否则返回转换得到的<see cref="System.Int32"/>整数。</remarks>
        public static int ParseInt(string s, int defValue)
        {

            //			try
            //			{
            //				if(s==null||s.Length==0)
            //					return defValue;
            //				return int.Parse(s);
            //			}
            //			catch(Exception)
            //			{
            //				return defValue;
            //			}
            //2005-06-14修改
            if (s == null || s.Length == 0)
                return defValue;
            if (!StrHelper.IsInt(s))
                return defValue;
            return int.Parse(s);


        }

        /// <summary>
        /// 将一个字符串转换为它的等效单精度浮点数。
        /// </summary>
        /// <param name="s"><see cref="string"/>，要转换的字符串</param>
        /// <param name="defValue"><see cref="float"/>，转换失败时返回的值。</param>
        /// <returns>返回指定字符串的等效单精度浮点数。</returns>
        /// <remarks>返回说明：如果指定的字符串无法转换为单精度浮点数则返回指定的默认值，否则返回转换得到的单精度浮点数。</remarks>
        public static float ParseFloat(string s, float defValue)
        {
            try
            {
                if (s == null || s.Length == 0)
                    return defValue;
                return float.Parse(s);
            }
            catch (Exception)
            {
                return defValue;
            }
        }

        public static double ParseDouble(string s, double defValue)
        {
            try
            {
                if (s == null || s.Length == 0)
                    return defValue;
                return double.Parse(s);
            }
            catch (Exception)
            {
                return defValue;
            }
        }

        /// <summary>
        /// 将一个字符串转换为它的等效十进制数
        /// </summary>
        /// <param name="s"><see cref="string"/>，要转换的字符串</param>
        /// <param name="defValue"><see cref="decimal"/>，转换失败时返回的值</param>
        /// <returns>返回指定字符串的等效十进制数</returns>
        /// <remarks>返回说明：如果指定的字符串无法转换为十进制数则返回指定的默认值，否则返回转换得到的十进制数。</remarks>
        public static decimal ParseDecimal(string s, decimal defValue)
        {
            //			try
            //			{
            //				if(s==null||s.Length==0)
            //					return defValue;
            //				return decimal.Parse(s);
            //			}
            //			catch(Exception)
            //			{
            //				return defValue;
            //			}
            //2005-06-14修改
            if (s == null || s.Length == 0)
                return defValue;
            if (!StrHelper.IsDecimal(s))
                return defValue;
            return decimal.Parse(s);
        }

        /// <summary>
        /// 将一个字符串转换为它的等效<see cref="System.DateTime"/>时间。
        /// </summary>
        /// <param name="s"><see cref="string"/>，要转换的字符串。</param>
        /// <param name="defValue"><see cref="System.DateTime"/>转换失败时返回的值。</param>
        /// <returns>返回指定字符串的等效<see cref="System.DateTime"/>时间。</returns>
        /// <remarks>返回说明：如果指定的字符串无法转换为<see cref="System.DateTime"/>时间则返回指定的默认值，否则返回转换得到的<see cref="System.DateTime"/>时间。</remarks>
        public static DateTime ParseDate(string s, DateTime defValue)
        {
            try
            {
                if (s == null || s.Length == 0)
                    return defValue;
                return DateTime.Parse(s);
            }
            catch (Exception)
            {
                return defValue;
            }
        }

        /// <summary>
        /// 将一个字符串转换为它的等效<see cref="System.Boolean"/>布尔类型的值。
        /// </summary>
        /// <param name="s"><see cref="string"/>，要转换的字符串。</param>
        /// <param name="defValue"><see cref="bool"/>，转换失败时返回的值。</param>
        /// <returns>返回指定字符串的等效<see cref="System.Boolean"/>布尔类型的值。</returns>
        /// <remarks>返回说明：如果指定的字符串无法转换为<see cref="System.Boolean"/>布尔类型的值则返回指定的默认值，否则返回转换得到的<see cref="System.Boolean"/>布尔类型的值。</remarks>
        public static bool ParseBool(string s, bool defValue)
        {
            if (s == null || s.Trim().Length == 0) return defValue;
            s = s.Trim().ToLower();
            if (s == "true") return true;
            else if (s == "false") return false;
            else return defValue;
        }

        /// <summary>
        /// 使用四舍五入的规则将十进制数舍入到指定的小数位数。
        /// </summary>
        /// <param name="d"><see cref="decimal"/>，要处理的十进制数。</param>
        /// <param name="decimals"><see cref="int"/>，指定数字要舍入到的小数位数，该值范围从0到28。</param>
        /// <returns>返回进行小数点舍入后的十进制数。</returns>
        /// <remarks>
        /// 此处采用四舍五入的规则进行小数位数的舍入，与<see cref="System.Decimal.Round"/>不同，<see cref="System.Decimal.Round"/>采用银行家算法进行小数位数的舍入。
        /// <para>返回说明：如果要处理的数字超出范围或者处理失败则返回decimal.Zero。</para>
        /// </remarks>
        public static decimal RoundDecimal(decimal d, int decimals)
        {
            try
            {
                if (d == decimal.Zero)
                    return decimal.Zero;

                //用于记录d的正负性
                int i = (d < decimal.Zero) ? -1 : 1;
                d = Math.Abs(d);
                if (decimals < 0 || decimals > 28)
                    decimals = 0;
                //throw new ArgumentOutOfRangeException("decimals", decimals, "要舍入的小数位数超出范围");

                //decimal不能大于Int64.MaxValue
                if (d > Int64.MaxValue)
                    return decimal.Zero;

                //设置允许的小数位
                if (d > 1)
                {
                    Int64 iAllowDecimals = Convert.ToInt64(Int64.MaxValue / (d * 10));
                    if (decimals > iAllowDecimals)
                        decimals = (int)iAllowDecimals;
                }

                decimal p = d * (decimal)Math.Pow(10, decimals);
                //判断数值是否过大
                if (p > Int64.MaxValue)
                {
                    return decimal.Zero;
                }
                Int64 intPart = Decimal.ToInt64(p);
                decimal decPart = p - intPart;
                if (decPart >= 0.5M)
                {
                    intPart += 1;
                }
                return i * intPart / (decimal)Math.Pow(10, decimals);
            }
            catch
            {
                return decimal.Zero;
            }
        }

        /// <summary>
        /// 判断某个数字的大小是否超过了允许的最大值。
        /// </summary>
        /// <param name="fValue"><see cref="decimal"/>，要进行判断的数字。</param>
        /// <param name="iMaxIntLength"><see cref="int"/>，允许的最大整数位大小（小数位超过时Oracle会自动进行截取）。</param>
        /// <returns>超出最大值返回true，否则返回false。</returns>
        public static bool IsOutOfSize(decimal fValue, int iMaxIntLength)
        {
            //说明：此函数主要判断fValue的整数部分是否超出指定的位数。

            //判断iMaxIntLength是否超出十进制的最大位数，这里限制为26位
            if (iMaxIntLength > 26 || iMaxIntLength < 0)
            {
                return true;
            }

            //获取fValue的整数部分的值
            decimal fTruncateValue = Math.Abs(decimal.Truncate(fValue));
            decimal fMaxValue = (decimal)Math.Pow(10d, (double)iMaxIntLength);
            return (fTruncateValue >= fMaxValue);

            //todo: 当前判断方法需要改进
            //if(fValue == decimal.MinValue)
            //	return false;
            //string sValue = fValue.ToString();
            //if(sValue.IndexOf(".") > -1)
            //  sValue = sValue.Split('.')[0];
            //return (sValue.Length > iMaxIntSize);
        }
        /// <summary>
        /// 序列化
        /// </summary>
        /// <param name="obj">要序列化的对象</param>
        /// <returns></returns>
        public static byte[] Serialize(object obj)
        {
            IFormatter formatter = new BinaryFormatter();
            MemoryStream stream = new MemoryStream();
            formatter.Serialize(stream, obj);
            return stream.ToArray();
        }
        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="bytes">字节数据</param>
        /// <returns></returns>
        public static object Deserialize(byte[] bytes)
        {
            MemoryStream stream = new MemoryStream(bytes);
            IFormatter formatter = new BinaryFormatter();
            return formatter.Deserialize(stream);
        }
        /// <summary>
        /// 将Base64的字符串格式化为标准的Url字符串
        /// </summary>
        /// <param name="sBase64String">Base64的字符串</param>
        /// <returns>标准的Url字符串</returns>
        public static string UrlEncodeBase64String(string sBase64String)
        {
            StringBuilder builder = new StringBuilder(sBase64String);
            builder.Replace("=", "_0").Replace("+", "_1").Replace("/", "_2");
            return builder.ToString();
        }
        /// <summary>
        /// 将标准的Url字符串转化为Base64的字符串
        /// </summary>
        /// <param name="sUrlEncodeString">标准的Url字符串</param>
        /// <returns>Base64的字符串</returns>
        public static string UrlDecodeBase64String(string sUrlEncodeString)
        {
            StringBuilder builder = new StringBuilder(sUrlEncodeString);
            builder.Replace("_0", "=").Replace("_1", "+").Replace("_2", "/");
            return builder.ToString();
        }
        /// <summary>
        /// 将对象序列化成Url上传输的标准字符串
        /// </summary>
        /// <param name="obj">对象</param>
        /// <returns>标准Url字符串</returns>
        public static string SerializeUrlString(object obj)
        {
            return UrlEncodeBase64String(Convert.ToBase64String(Serialize(obj)));
        }
        /// <summary>
        /// 将Url上传输的标准字符串反序列化成对象
        /// </summary>
        /// <param name="sUrlString">标准Url字符串</param>
        /// <returns>对象</returns>
        public static object DeserializeUrlString(string sUrlString)
        {
            return Deserialize(Convert.FromBase64String(UrlDecodeBase64String(sUrlString)));
        }
    }
}
